import { Metadata_Key } from "../../constants";

/** 方法前置增强 */
export const MethodBeforeAdvice = Symbol('MethodBeforeAdvice');
/** 方法正常执行后的后置增强 */
export const MethodAfterReturningAdvice = Symbol('MethodAfterReturningAdvice');
/** 方法执行异常时的后置增强 */
export const MethodAfterThrowingAdvice = Symbol('MethodAfterThrowingAdvice');
/** 方法执行完成之后的增强(无论方法执行是否发生异常) */
export const MethodAfterAdvice = Symbol('MethodAfterAdvice');
/** 方法执行前后的环绕增强 */
export const MethodAroundAdvice = Symbol('MethodAroundAdvice');

/** AOP增强基础接口 */
export interface Advice {
  /**
   * 定义AOP调用顺序，由小到大
   */
  order: number;

  /**
   * 过滤AOP切入点
   * @param target      被切入的目标实例对象
   * @param property    被切入目标对象的属性(或者方法)名称
   * @param argsOrValue 被切入目标对象的属性值或者方法参数
   * @returns           true:需要AOP切入增强，false:不做增强
   */
  filterPointcut(target: any, property: PropertyKey, argsOrValue?: any): boolean;
}

/** 方法前置增强 */
export interface MethodBeforeAdvice extends Advice {
  /**
   * 方法前置增强逻辑
   * @param target 被切入的目标实例对象
   * @param method 被切入目标对象的方法名称
   * @param args   方法调用参数
   */
  before(target: any, method: PropertyKey, args: any[]): void;
}

/** 方法正常执行后的后置增强 */
export interface MethodAfterReturningAdvice extends Advice {
  /**
   * 方法正常执行后的后置增强逻辑
   * @param target      被切入的目标实例对象
   * @param method      被切入目标对象的方法名称
   * @param args        方法调用参数
   * @param returnValue 方法执行完成的返回值
   * @returns
   */
  afterReturning(target: any, method: PropertyKey, args: any[], returnValue: any): void;
}

/** 方法执行异常时的后置增强 */
export interface MethodAfterThrowingAdvice extends Advice {
  /**
   * 方法执行异常时的后置增强逻辑
   * @param target  被切入的目标实例对象
   * @param method  被切入目标对象的方法名称
   * @param args    方法调用参数
   * @param error   异常时的错误对象
   * @returns
   */
  afterThrowing(target: any, method: PropertyKey, args: any[], error: any): void;
}

/** 方法执行完成之后的增强(无论方法执行是否发生异常) */
export interface MethodAfterAdvice extends Advice {
  /**
   * 方法执行完成之后的增强逻辑(无论方法执行是否发生异常)
   * @param target        被切入的目标实例对象
   * @param method        被切入目标对象的方法名称
   * @param args          方法调用参数
   * @param successful    方法执行是否正常完成
   * @param returnValue   方法执行完成的返回值
   * @param error         异常时的错误对象
   */
  after(target: any, method: PropertyKey, args: any[], successful: boolean, returnValue?: any, error?: any): void;
}

/** 方法执行前后的环绕增强 */
export interface MethodAroundAdvice extends Advice {
  /**
   * 方法前置增强逻辑
   * @param target 被切入的目标实例对象
   * @param method 被切入目标对象的方法名称
   * @param args   方法调用参数
   * @returns      如果是环绕增强可以返回aroundContext(用于在before和after之间访问同一个上下文变量)
   */
  before(target: any, method: PropertyKey, args: any[]): BreakOrContext;

  /**
   * 方法执行完成之后的增强逻辑(无论方法执行是否发生异常)
   * @param target        被切入的目标实例对象
   * @param method        被切入目标对象的方法名称
   * @param args          方法调用参数
   * @param successful    方法执行是否正常完成
   * @param returnValue   方法执行完成的返回值
   * @param error         异常时的错误对象
   * @param aroundContext 用于在before和after之间访问同一个上下文变量
   * @returns             如果异常是否需要抛出error异常(默认抛出异常，返回false表示"吃掉"异常)
   */
  after(target: any, method: PropertyKey, args: any[], successful: boolean, returnValue?: any, error?: any, aroundContext?: any): false | void;
}

/** 环绕增强的before返回值 */
export interface BreakOrContext {
  /** 是否需要提前返回 */
  isReturn: boolean;
  /**
   * isBreak=true: 表示提前返回的值<br />
   * isBreak=false: 表示before和after之间访问同一个上下文变量<br />
   */
  context?: any;
}

/** AOP组件排序 */
export function sortAdvice(a: Advice, b: Advice) {
  const intA = a ? a.order || 0 : 0;
  const intB = b ? b.order || 0 : 0;
  const res = intA - intB;
  return res > 0 ? 1 : res < 0 ? -1 : 0;
}

/** 判断AOP组件是否存在Aspect Metadata*/
export function hasAspectMetadata(advice: Advice): boolean {
  let flag = false;
  if (advice && (<any>advice).__proto__?.constructor) {
    const aspectMetadata = Reflect.getMetadata(Metadata_Key.aspect, (<any>advice).__proto__?.constructor);
    if (aspectMetadata) {
      flag = true;
    }
  }
  return flag;
}
